/**
 * ObjectId 工具类
 * 参考MongoDB的ObjectId格式: 4字节时间戳 + 5字节随机数 + 3字节计数器
 */
export class ObjectId {
  private static counter = BigInt(0);
  private static maxCounter = BigInt(0xFFFFFF); // 3字节最大值
  private static machineId: number;
  private static processId: number;

  static {
    // 初始化机器码和进程ID
    this.machineId = Math.floor(Math.random() * 0xFFFFFF);
    this.processId = Math.floor(Math.random() * 0xFFFF);
  }

  /**
   * 生成ObjectId
   * @returns ObjectId字符串
   */
  public static generate(): string {
    try {
      // 时间戳部分 - 4字节
      const timestamp = Math.floor(Date.now() / 1000);
      const timestampHex = timestamp.toString(16).padStart(8, '0');

      // 随机值部分 - 5字节(使用机器ID 3字节 + 进程ID 2字节)
      const machineHex = this.machineId.toString(16).padStart(6, '0');
      const processHex = this.processId.toString(16).padStart(4, '0');

      // 计数器部分 - 3字节，使用BigInt避免超出最大安全整数范围
      this.counter = (this.counter + BigInt(1)) % this.maxCounter;
      const counterHex = this.counter.toString(16).padStart(6, '0');

      return timestampHex + machineHex + processHex + counterHex;
    } catch (error) {
      console.error('生成ObjectId失败', error);
      return this.unsafe();
    }
  }

  /**
   * 从ObjectId获取创建时间
   * @param objectId ObjectId字符串
   * @returns 创建时间的Date对象
   */
  public static getTimestamp(objectId: string): Date {
    if (!this.isValid(objectId)) {
      throw new Error('无效的ObjectId');
    }
    
    const timestamp = parseInt(objectId.substring(0, 8), 16);
    return new Date(timestamp * 1000);
  }

  /**
   * 验证ObjectId是否有效
   * @param objectId 需要验证的ObjectId字符串
   * @returns 是否有效
   */
  public static isValid(objectId: string): boolean {
    return typeof objectId === 'string' && /^[0-9a-f]{24}$/i.test(objectId);
  }

  /**
   * 生成不安全的ObjectId（用于出错时的备选方案）
   * @returns ObjectId字符串
   */
  private static unsafe(): string {
    const timestamp = Math.floor(Date.now() / 1000).toString(16).padStart(8, '0');
    let rest = '';
    
    for (let i = 0; i < 16; i++) {
      rest += Math.floor(Math.random() * 16).toString(16);
    }
    
    return timestamp + rest;
  }
} 